/**
 * <AiRmob-Framework; A simple augmented reality framework for android >
    Copyright (C) <2012>  <AiRmob Team>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package airmob.framework;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import android.content.Context;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
import android.os.SystemClock;
import android.view.SurfaceHolder;

/**
 * This class wraps some methods to supply an easier handling of the Android Cameradevice
 * @author nlichtenberg
 *
 */
public class AiRmobCamera {
	
	
	// define camerasettings
	public static int CAMERA_LOW = 20;
	public static int CAMERA_MID = 21;
	public static int CAMERA_HIGH = 22;
	
	private static List<Size> mSupportedSizes;
	private Camera mCam;
	public int width, height;
	AiRmobActivity context;
	
	public AiRmobCamera(AiRmobActivity context){
		this.context = context;
	}
	
	/**
	 * Try to open a front-facing camera
	 * @return
	 */
	public void openFrontFacingCamera() 
    {
        int cameraCount = 0;        
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        cameraCount = Camera.getNumberOfCameras();
        for ( int camIdx = 0; camIdx < cameraCount; camIdx++ ) {
            Camera.getCameraInfo( camIdx, cameraInfo );
            if ( cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT  ) {
                try {
                    mCam = Camera.open( camIdx );
                } catch (RuntimeException e) {
                    
                }
            }
        }       
    }
    
	/**
	 * 
	 * @return A direct reference to the Android.Camera object.
	 */
	public Camera getDevice(){
		return mCam;
	}
	
	/**
	 * Returns a Cameraobject with status 'open'
	 * @return
	 */
	public void openSimpleCamera(){
		mCam = Camera.open();	
	}
	
	/**
	 * Try to open a back-faceing camera
	 * @return
	 */
    public void openBackFacingCamera() 
    {
        int cameraCount = 0;
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        cameraCount = Camera.getNumberOfCameras();
        for ( int camIdx = 0; camIdx < cameraCount; camIdx++ ) {
            Camera.getCameraInfo( camIdx, cameraInfo );
            if ( cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK  ) {
                try {
                    mCam = Camera.open( camIdx );
                } catch (RuntimeException e) {
                    
                }
            }
        }
    }
    


/**
 * 
 * @param sizes
 * @param w
 * @param h
 * @return a List of preview sizes best fitting to the screen's aspect ratio
 */

private List<Size> getOptimalPreviewSize(List<Size> sizes, int w, int h) {	   
	   
	   // Code von Google (abgewandelt)
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) w / h;
        if (sizes == null) return null;

        Size optimalSize = null;
        List<Size> matchingSizes = new ArrayList<Size>();
        matchingSizes.clear();
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        // Try to find an size match aspect ratio and size
        for (Size size : sizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                matchingSizes.add(optimalSize);
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        // Cannot find the one match the aspect ratio, ignore the requirement
        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    matchingSizes.add(optimalSize);
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return matchingSizes;
    }
   
   public float getCameraHorizontalAngle(){
	   Camera.Parameters params = mCam.getParameters();
	   return params.getHorizontalViewAngle();
   }
   
   public float getCameraVerticalAngle(){
	   Camera.Parameters params = mCam.getParameters();
	   return params.getHorizontalViewAngle();
   }
   
  
   /**
    * Initialises the camera, setting the preview size to a fitting aspect ratio, when using predefined constants
    * @param sh
    * @param width
    * @param height
    * @param cam
    * @param sizeID sets the previews quality. 20 = low, 21 = mid, 22 = high. If there are only 2 possible sizes, the higher resolution is chosen "mid". 
    * Any other input is possible to select the resolution manually.
    */
   public void startVideoStream (SurfaceHolder sh, int width, int height, int sizeID){
	   
	   Camera.Parameters p = mCam.getParameters();
	   mSupportedSizes = p.getSupportedPreviewSizes();
	   if(sizeID >= mSupportedSizes.size() && sizeID < 20 || sizeID > 22) // -> sizeID has an invalid value
			  sizeID = 0;
	   
	   
	  if(sizeID >= 20){
		  sizeID -= 20;
		  // sizeID only accepts 0,1,2
		  if(sizeID > 2 || sizeID < 0)
			  sizeID = 0;	 
		  
	   
		  
		  List<Camera.Size> mPreviewSizes = getOptimalPreviewSize(mSupportedSizes, width, height);
		  sizeID = (sizeID * mPreviewSizes.size()) / 2;
		  if(sizeID >= mPreviewSizes.size())
			  sizeID = mPreviewSizes.size() - 1;
		  this.width = mPreviewSizes.get(sizeID).width;
		  this.height = mPreviewSizes.get(sizeID).height;
		  p.setPreviewSize(this.width, this.height);		
	  }
	  else{
		  	p.setPreviewSize(mSupportedSizes.get(sizeID).width, mSupportedSizes.get(sizeID).height);
			this.width = mSupportedSizes.get(sizeID).width;
			this.height = mSupportedSizes.get(sizeID).height;
	  }
		
		
		mCam.setParameters(p);		 
		try {
			mCam.setPreviewDisplay(sh);
		} catch (Exception e) {					
		}		
		mCam.startPreview();
   }
   

   
   
   /**
    * Stops the video-livestream. The camera releases its callbackmethod, stops the previewscreen and will be released itself.
    */
   public void stopVideoStream(){
	   
	   context.pipeline.continueTracking = false;
	   mCam.setPreviewCallback(null);
	   mCam.stopPreview();		
	   mCam.release();
	   mCam = null;
   }
   
   
   
   /**
    * 
    * @return String that contains all supported previewsizes
    */
   public String getSupportedSizes(){	   
	   StringBuilder stringBuilder = new StringBuilder();
	    String ls = System.getProperty("line.separator");
	   for(int i = 0; i < mSupportedSizes.size(); i++){
		   stringBuilder.append( i + ": width " + Integer.toString(mSupportedSizes.get(i).width) + "; height: " + Integer.toString(mSupportedSizes.get(i).height) + "; ratio: " + 
		   Float.toString((float)mSupportedSizes.get(i).width/mSupportedSizes.get(i).height));
		   stringBuilder.append( ls );
	   } 	    
	   return stringBuilder.toString();	   
   }    
   
   
   public void startTracking(){
	   context.pipeline.continueTracking = true;
	   context.pipeline.startPipeline(mCam);
   }
   
   public void stopTracking(){
	   context.pipeline.continueTracking = false;
	   context.pipeline.setPipelineMode(Pipelinehandler.MODE_STOP_TRACKING);
   }
   
   
}



